test(reservations): M2 test-coverage backfill (7 of 8 items) - #4280
Merged
piotr-roslaniec merged 18 commits intoSep 3, 2026
Conversation
Closes gap-analysis Major row 1 and implementation-plan.md M1 row 3. ReservationAnchorProposal, ReservedRedemptionProposal, ReservationReanchorProposal, and ReservationDissolutionProposal previously used a JSON Marshal/Unmarshal placeholder, unlike every other CoordinationProposal type in this package (Heartbeat, DepositSweep, Redemption, MovingFunds, MovedFundsSweep), which all marshal via pkg/tbtc/gen/pb. Added the four missing message types to message.proto and regenerated message.pb.go (protoc 3.21.12 installed for this). Moved the four proposals' Marshal/Unmarshal from reservation.go's JSON stubs into marshaling.go, matching the existing proto-based implementations' structure and field-encoding conventions (big.Int fees via .Bytes()/SetBytes(), fixed-size hashes/pubkey-hashes via byte-slice copy with a length check). Preserved the original JSON stubs' validation intent under proto3's zero-value-is-absence semantics: a request nonce of 0, or empty fee/reservation-key/hash bytes, are rejected the same way an explicitly-missing JSON field was. The original '== nil' checks on *big.Int fields don't carry over as-is - SetBytes never returns nil - so they're now byte-length checks on the wire field instead, which is the pattern every other proto-based proposal in this file already uses. Testing: extended the existing table-driven TestCoordinationMessage_MarshalingRoundtrip with the four new types (exact field-for-field equality through the wire, matching the existing test's own precision, not just the fuzz-style tests already covering every sibling type) plus four new TestFuzzCoordinationMessage_MarshalingRoundtrip_With<X>Proposal crash-safety tests, matching the one-per-type convention. Rewrote the pre-existing TestReservationProposals_UnmarshalRejectsMissingIntegers (now TestReservationProposals_UnmarshalRejectsInvalidFields) to construct real protobuf payloads instead of JSON string literals, porting every original missing-field case plus two new structural cases (invalid hash/pubkey-hash length) that fall out of the new wire format. go test ./pkg/tbtc/...: 15/15 new/changed tests pass, full package suite passes (146s), -race clean (156s). gofmt/vet clean on all 6 changed files.
Blocker gap found while preparing the M3 multi-signer integration test: pkg/tbtc/coordination.go's getActionsChecklist decides which WalletActionTypes a coordination round even considers, and it never emitted ActionReservationAnchor or ActionReservationReanchor. pkg/tbtcpg.ProposalGenerator.Generate only runs a task whose ActionType() appears in the checklist it's handed (tbtcpg.go:124-135) - it never iterates pg.tasks directly. NewReservationAcceptanceTask and NewReservationReanchorTask are registered when config.Reservations.Enabled=true (tbtcpg.go:88-92), but with no checklist entry, Generate's per-window loop never selected them. Both tasks were structurally unreachable in production regardless of PR #4276/#4277's fixes. Every existing unit test for these tasks (reservation_acceptance_test.go, reservation_reanchor_test.go) calls task.Run(request) directly, bypassing getActionsChecklist/Generate entirely - which is why this was never caught by any prior PR's test suite. Fix: ActionReservationAnchor and ActionReservationReanchor are now appended unconditionally, checked on every coordination window like ActionRedemption (both are custody-critical - an unaccepted reservation or a stale re-anchor risks stranding, not just reduced throughput - unlike the frequency-gated sweep/moving-funds actions). A node with reservations disabled safely no-ops on these: Generate already treats a checklist action with no matching registered task as 'unsupported' and skips it without error (tbtcpg.go:131-135), the same mechanism that already gates every other optional per-node task. Testing: - Updated TestCoordinationExecutor_GetActionsChecklist and its _PostActivation sibling: every non-nil expected checklist now includes both new actions right after ActionRedemption, matching the real append order. Extended assertChecklistOrdering's priority map accordingly (Redemption=0, ReservationAnchor=1, ReservationReanchor=2, then the existing sweep/moving-funds/ heartbeat priorities shifted). - Added TestCoordinationExecutor_GetActionsChecklist_ReservationActionsAlwaysPresent, a dedicated regression guard asserting both actions are present across pre/post-activation and 4th/non-4th windows, decoupled from the large table-driven test - would fail on its own if this wiring regresses. - go test ./pkg/tbtc/... ./pkg/tbtcpg/...: 476/476 pass. - go build ./... && go test ./...: full repo, 49 packages, zero FAIL. - gofmt/vet clean on both changed files.
…oordination Implementation-plan.md Milestone 3, 'multi-signer simulated integration test' item (per user decision: build the test, leave the testnet-drill item as an agent-not-actionable tracked item since it needs live infra and calendar time, not code). Scales TestCoordinationExecutor_Coordinate's existing 3-operator harness - deterministic keypairs, real per-operator localChain fakes, a real shared netlocal.BroadcastChannel, one goroutine per operator running coordinationExecutor.coordinate concurrently - to ReservationAnchorProposal and ReservationReanchorProposal. This exercises the real leader/follower coordination round-trip (checklist generation -> leader election -> broadcast -> follower validation -> convergence) that no mocked pkg/tbtcpg unit test can cover, since those call task.Run(request) directly and never go through coordinationExecutor.coordinate. It also exercises PR #4277's protobuf marshaling of both proposal types over a real wire round-trip, since every follower unmarshals the leader's broadcast coordinationMessage. Depends on PR #4278 (this branch's parent): before that fix, ActionReservationAnchor/ActionReservationReanchor never appeared in getActionsChecklist's output, so every operator's checklist search in these tests would fall through to NoopProposal and fail - confirmed by temporarily reverting the checklist fix and re-running (both new tests failed with the expected NoopProposal mismatch), then restoring it. Found and fixed one bug in this test's own harness during verification: both new tests initially shared one netlocal broadcast channel name. getBroadcastChannel's registry is keyed by name and never releases old channels, so under -race (which changed goroutine/channel-delivery timing enough to surface it in ~every run), the reanchor test's follower sometimes received a stale broadcast left over from the anchor test's leader. Fixed by giving each test its own channel name; re-verified stable across 10 repeated -race runs plus the full non-race and race suites. Testing: - go test ./pkg/tbtc/...: 365/365 pass. - go test -race ./pkg/tbtc/...: clean, no data races, including -count=10 on just the two new tests. - go build ./... && go test ./...: full repo, 49 packages, zero FAIL. - gofmt -l / go vet: clean.
Implementation-plan.md Milestone 2, all items except the one item (ValidateReservationAnchorProposal/ValidateReservationReanchorProposal tests) that needs simulated-backend test infrastructure this repository doesn't have - see docs/spec/reservations/m1-keep-core- readiness/01-gap-analysis.md's new Minor row for that finding, and the item-8 dedup row for the golden-value equivalence decision. pkg/tbtc/reservation_test.go: - TestAssembleReservationAnchorTransaction: happy-path output shape (1-in-1-out, deposit value minus fee, P2WPKH to the target wallet). Also doubles as the golden reference for pkg/tbtcpg's dedup test. - TestAssembleReservationReanchorTransaction: same shape assertion for the re-anchor sibling. pkg/chain/ethereum/tbtc_test.go: - TestConvertReservationParametersFromAbiType: full 10-tuple field mapping, every field set to a distinct non-zero value so a swapped or dropped field can't hide behind a shared zero default. - TestConvertReservationFromAbiType_DropsCumulativeReanchorFee: pins the intentional CumulativeReanchorFee omission and verifies every other field maps correctly around it. pkg/tbtcpg/reservation_acceptance_test.go: - TestReservationAcceptanceTask_BoundaryChecks: 6-case table (at-limit accepts / one-over rejects) for MaxReservationsPerWallet, ReservationMinAmount, and ReservationMaxTotalAmount - BoundedLookback only ever used these as fixture data, never at the actual boundary. - TestReservationAcceptanceTask_ReservationParametersFetchedLive: runs the same task twice against the same deposit, mutating ReservationMinAmount between calls - proves ReservationParameters() is fetched live per call, not cached on the task. pkg/tbtcpg/reservation_anchor_dedup_test.go (new file, package tbtcpg - internal, not tbtcpg_test - to reach the unexported function): - TestBuildReservationAnchorTransaction_MatchesPkgTbtcGoldenOutput: buildReservationAnchorTransaction (pkg/tbtcpg) and assembleReservationAnchorTransaction (pkg/tbtc) are independently maintained copies of the same logic, both unexported in different packages - Go's visibility rules make a single test calling both impossible without a production-code change. This test and pkg/tbtc's TestAssembleReservationAnchorTransaction instead pin the identical golden input/output values (deposit 100000, fee 1500, output 98500) in each package, catching either copy drifting from the other without eliminating the underlying duplication (real fix deferred, per decision this session). Testing: - go test ./pkg/tbtc/... ./pkg/tbtcpg/... ./pkg/chain/ethereum/...: 519/519 pass. - go build ./... && go test ./...: full repo, 49 packages, zero FAIL. - gofmt -l / go vet: clean on all 4 changed/new files.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The earlier regeneration used whatever protoc/protoc-gen-go happened to be installed (apt's protoc 3.21.12, go install's protoc-gen-go v1.30.0 at HEAD) - neither matches this file's own prior stamp (protoc-gen-go v1.28.0 / protoc v3.19.4) or any other .pb.go in the repo (every other generated file is v1.28.0 or v1.28.1 / protoc 3.7.1-3.21.5; this file was the only v1.30.0/3.21.12 outlier). Regenerated with protoc 3.19.4 (official release zip, not apt) and protoc-gen-go v1.28.0 (go install pinned to that version). Diff against the previous commit is exactly the two version-stamp lines - message content is otherwise byte-identical, confirming the earlier regeneration was semantically correct and this is a pure toolchain pin, not a functional change.
…-coordination-checklist
…ion-multisigner-integration-test
…servation-test-coverage-backfill
…tobuf-marshaling Reconciles this branch's protobuf-marshaling migration for reservation proposals with the review-fix commit that landed independently on the base branch, which performed the same migration plus additional correctness fixes and a scope narrowing. - message.proto/message.pb.go: drop ReservedRedemptionProposal and ReservationDissolutionProposal (base commit 757c6d8: 'scaffolding that ships with no producer, dispatch case, or wired validator in this milestone' - M1 is creation/custody/re-anchor only per the variant-B scope decision); regenerated message.pb.go from the resolved .proto with the pinned protoc/protoc-gen-go toolchain. - reservation.go/marshaling.go: adopt the base's Marshal/Unmarshal for the two surviving types (anchor, re-anchor), which restores the all-zero TargetWalletPublicKeyHash guard this branch's independent migration had not carried; drop this branch's now-superseded duplicate implementation and the two removed types' scaffolding; added back the doc comments the base's version had dropped, matching every other proposal type's convention in the file. - reservation_test.go: adopt the base's renamed, restructured TestReservationProposals_UnmarshalRejectsMissingIntegers (matching its 2-arg marshalPb(t, msg) helper and null-payload style); restored two byte-length validation test cases ('invalid deposit funding tx hash length', 'invalid target wallet public key hash length') the base's version had dropped despite the corresponding Unmarshal checks still being live - both now pass against the merged validation order. - marshaling_test.go: dropped the two removed types' table entries and dedicated fuzz round-trip tests; fixed a duplicate proto import left by the auto-merge. Full repo: zero FAIL.
…-coordination-checklist Reconciles this branch's original coordination-checklist wiring with the review-fix commit that independently re-fixed the same wiring on the base branch (757c6d8: 'gate reservation checklist entries solely on the activation block, not the local per-operator config flag'). - coordination.go: the auto-merge silently duplicated the reservation checklist append (this branch's original unconditional append, right after Redemption, plus the base's corrected activation-block-and-frequency-gated append later in the function) - ActionReservationAnchor/ActionReservationReanchor would have been listed twice whenever both conditions held, and listed even before activation the rest of the time. Removed this branch's unconditional append; kept only the base's gated one. - coordination_test.go: adopted the base's TestCoordinationExecutor_GetActionsChecklist_Reservations (activation + frequency gating) and its updated priority ordering (Redemption < DepositSweep < MovedFundsSweep < MovingFunds < ReservationAnchor < ReservationReanchor < Heartbeat, reservations now gated like the sweep actions rather than unconditional). Rewrote this branch's two pre-existing checklist tests (TestCoordinationExecutor_GetActionsChecklist, _PostActivation) to match: expected values recomputed by running the corrected production getActionsChecklist against each test's exact block/window inputs, not hand-derived - 24 subtests total, all independently verified against the same fixed implementation the regression guard exists to catch drift in. Full repo: zero FAIL.
…ion-multisigner-integration-test Auto-merged cleanly on coordination_test.go (this branch's own additions - the multi-signer integration tests and their fixtures - land purely after the base's content with zero line overlap), but the merge exposed a real breakage in this branch's own new tests: both TestCoordinationExecutor_Coordinate_ReservationAnchor and _ReservationReanchor used coordinationBlock = 900, which predates ReservationsActivationBlock (24559289) fixed on the base branch (757c6d8). With the base's activation-block gate now in effect, ActionReservationAnchor/ActionReservationReanchor never appear in the checklist at block 900, so both tests' mock proposal generators fell through to NoopProposal and failed. Bumped both tests' coordinationBlock to 24559289+3511=24562800 (a post-activation, 4th-coordination-window block, verified against the same production getActionsChecklist this session already confirmed correct on the base branch). Leader election is unaffected by this change and stays at operator2: the coordination seed depends only on the wallet public key hash and the injected safe-block hash - both fixed regardless of which raw block number the test passes in - not on the coordination block number itself. Corrected newReservationCoordinationWallet's doc comment, which had claimed leader election was tied to block 900 specifically. Full repo: zero FAIL.
…servation-test-coverage-backfill Reconciles this branch's M2 test-coverage backfill work with several independent changes on the base: dead-code removal, the conversion function rename, and a new net-of-fee minimum-amount gate. - Deleted pkg/tbtcpg/reservation_anchor_dedup_test.go entirely: its sole test pinned buildReservationAnchorTransaction (tbtcpg package) against a golden value shared with pkg/tbtc's sibling function, to catch drift between the two independently-maintained duplicate implementations. c48d17b deleted buildReservationAnchorTransaction from production code (reservation_acceptance.go now derives the reservation key from the anchor UTXO instead of reassembling the transaction) - the duplication this test existed to guard against no longer exists, so the guard is moot, not just broken. - pkg/tbtc/reservation_test.go: fixed two tests (TestAssembleReservationAnchorTransaction, TestAssembleReservationReanchorTransaction) left calling the pre-rename lowercase assembleReservation{Anchor,Reanchor}Transaction; both functions were exported and gained a required *ReservationAction parameter (fee-ceiling guard) upstream. Updated call sites and doc comments; both tests' own value remains real (happy-path output-shape coverage the nearby InputValidation test doesn't provide). - pkg/chain/ethereum/tbtc_test.go: this branch and the base independently added TestConvertReservationParametersFromAbiType with different fixture values (both equally rigorous); kept this branch's version (doc-linked to the gap-analysis finding it backfills) and the base's two genuinely new, non-overlapping tests (TestConvertReservationFromAbiType, TestConvertReservationActionFromAbiType). - pkg/tbtcpg/reservation_acceptance_test.go: same collision pattern - this branch's TestReservationAcceptanceTask_ReservationParametersFetchedLive and the base's TestReservationAcceptanceTask_GetWalletError are unrelated tests that landed at the same insertion point; kept both. Fixed three latent fixture gaps this merge surfaced in this branch's own ReservationParametersFetchedLive/BoundaryChecks tests (pre-dating this merge, never run against current production code until now): missing EndBlock on the DepositRevealedEventFilter, missing Vault on the DepositRevealedEvent (both added by c48d17b's EndBlock/vault- match hardening), and a missing fee-oracle rate (SetEstimateSatPerVByteFee) now required by dynamic fee estimation. Also corrected the 'exactly at minimum' boundary case's deposit amount: proposeReservationAcceptance now enforces the minimum against the *net-of-fee* anchor value, not the gross deposit amount, so the boundary must be reservationMinAmount + the fixture's deterministic 710-sat fee, not reservationMinAmount itself. Full repo: zero FAIL.
7 tasks
piotr-roslaniec
force-pushed
the
m1/reservation-multisigner-integration-test
branch
from
September 3, 2026 11:09
cd64125 to
4b7ee24
Compare
Fixes 18 confirmed findings from the PR #4280 review (agent-docs/reviews/pr-4280/) plus corrections for merge-step data defects found while auditing the raw per-lens recall against the synthesized findings.json: pkg/tbtcpg/reservation_acceptance_test.go: - Fix tautological ReservationParametersFetchedLive test: ReservationParameters() now returns a deep copy so test-side mutation cannot corrupt the production candidate cache; add a no-mutation control subtest. - Add TestReservationAcceptanceTask_AnchorTransactionAssembly to lock the anchor-transaction wiring (output script/value) that was previously entirely untested. - Add TestReservationAcceptanceTask_GetReservationError pinning the current GetReservation-error fallthrough behavior. - Extend BoundaryChecks with a gross-passes/net-fails row and at-limit/ one-over coverage for the three previously untested eligibility gates (single-deposit, aggregate-wallet-amount, active-count caps). - Extract setupEligibleDeposit helper, removing 6x fixture duplication and inert reservedDeposits assignments. - Strengthen BoundedLookback to assert the specific drop path. - Correct doc-comment line citations and restore GetWalletError's comment. - Name the 710 sat fee constant with an accurate derivation comment. pkg/tbtc/reservation_test.go: - Fix TestAssembleReservationAnchorTransaction to use a distinct target wallet (was tautologically reusing the deposit's source wallet hash). - Correct docstrings overstating what prior tests did NOT cover; drop the dangling external gap-analysis doc references. - Note the tbtcpg-side reanchor-assembly coverage gap in the test docstring. pkg/chain/ethereum/tbtc_test.go: - Restore named vaultAddress variable (DRY regression). - Pad address literals to 40 hex digits (silent zero-left-pad bug). - Fix wrong doc-comment citation for the CumulativeReanchorFee omission rationale. - Replace tautological expected-value recomputation with literal constants. - Restore domain-meaningful fixture values in place of a meaningless arithmetic sequence. - Drop dangling external gap-analysis doc reference. go build ./..., go vet, gofmt, and go test ./... (49 packages) all clean.
…ration-test' into m1/reservation-test-coverage-backfill # Conflicts: # pkg/tbtc/coordination.go # pkg/tbtc/coordination_test.go # pkg/tbtc/marshaling_test.go # pkg/tbtc/reservation_test.go # pkg/tbtcpg/reservation_acceptance_test.go
Base automatically changed from
m1/reservation-multisigner-integration-test
to
m1/reservation-coordination-checklist
September 3, 2026 12:34
Base automatically changed from
m1/reservation-coordination-checklist
to
reservations-epic
September 3, 2026 13:02
A candidate deposit that cleared the gross ReservationMinAmount but failed the net-of-fee check (or didn't cover the anchor fee at all) was returned as the selected candidate and then aborted proposal generation with a hard error. Nothing marked the deposit ineligible after the abort, so the same doomed deposit was re-selected on every subsequent Run(), permanently blocking the wallet's reservation queue until the deposit-reveal event aged out of the ~30-day look-back window. Move both fee viability checks (anchor fee coverage and net-of-fee minimum) into findReservationAcceptanceCandidate's selection loop, so a candidate that fails either check is skipped in favor of the next one instead of halting the pipeline. proposeReservationAcceptance now reuses the fee already computed during selection instead of recomputing and re-validating it. Also documents (does not change) the pre-existing fail-open handling of GetReservation errors: its interface contract signals "not found" as an error, so failing closed there would reject every brand-new candidate; a RequestNonce==1 assertion was added to TestReservationAcceptanceTask_GetReservationError to pin the intentional fallback.
Fixes 20 P1/P2/P3 findings from a multi-agent review of this PR's own test coverage: pkg/tbtcpg/reservation_acceptance_test.go: - Narrow the PastDepositRevealedEvents override to swallow only its claimed sentinel; add error-injection coverage. - Assert AnchorTransactionAssembly's returned proposal fields against the task-derived candidate, not just a hand-built fixture object; make the fixture's ValidateReservationAnchorProposal genuinely check the funding outpoint instead of always returning nil. - Delete the fixture's shadow ReservationParameters override/field; route through the base LocalChain's already-correct value-copy setter/getter instead. - Fix a test comment overclaiming exclusion is "strictly attributable" to one filter when multiple fixture gaps independently cause it. - Make the freshness-control test's RequestReservationAcceptance override actually record an event, so the dedup guard it's meant to exercise engages for real. - Pin RequestNonce==1 for the documented GetReservation fail-open path. - Make BoundaryChecks' three cap fields pointer-typed so a row can express production's real "0 = unlimited" semantic; add rows proving it. - Delete the dead reservedDeposits field/IsReservedDeposit override (zero production readers). - Add TestReservationAcceptanceTask_ValidateProposalError covering the previously-unexercised validateErr wrapping path. - Fix two misleading comments (wrong function attribution for the ReservationMinAmount gate; wrong description of the GetWallet error contract). - Add testReservationVaultAddress const, replacing 30+ inline literal duplicates. - Extract newBoundaryTestChain helper, replacing a ~13-line setup block duplicated across 16 call sites. pkg/tbtcpg/chain_test.go: - Fix binary.BigEndian.PutUint64 writing EndBlock bytes into the startBlock buffer instead of endBlock across 5 call sites, so EndBlock actually contributes to the event-filter cache key. pkg/chain/ethereum/tbtc_test.go: - Replace a stale tbtc.go line-range doc citation with a symbol reference. - Fold TestConvertReservationFromAbiType_DropsCumulativeReanchorFee into TestConvertReservationFromAbiType as a subtest, matching this file's one-test-per-converter convention. pkg/chain/ethereum/tbtc.go: - Add an in-tree TODO marking ValidateReservationAnchorProposal's deferred test coverage, since the docs describing that deferral don't exist in this checkout.
piotr-roslaniec
added a commit
that referenced
this pull request
Sep 3, 2026
…nt review of #4282) (#4283) ## Summary Remediation for the 37 confirmed findings from a multi-agent review of PR #4282 (`dev` <- `reservations-epic`, i.e. the accumulated content of #4274+#4276+#4277). 37 raised -> 37 confirmed -> 0 dropped after arbitration and validation. - **P1 (6 of 7 fully fixed, 1 partially fixed):** deposit-sweep reservation-vault exclusion, reservation look-back underflow + target-wallet check, reservation acceptance `eth_getLogs` bounds + nonce reconciliation + caps, SPV proof-loop retry-eviction data loss (symptom fixed, structural root cause deferred - see below), stale-deposit timeout memoization, below-dust re-anchor trigger removal (M-27, resolved via tbtc-v2 source after user escalation). - **P2/P3 (22 of 30 fixed, 8 explicitly deferred):** see "Deferred" below. Full-repo `go build`, `go vet`, and `go test ./...` all pass with these fixes applied (verified after every commit and once more at closeout). ## Deferred (1 P1 architectural root-cause + 7 P2/P3 symptoms/hygiene) An arbiter-recommended structural fix for M-16 (remove the SPV proof loop's persistent-cursor design entirely in favor of the stateless bounded-rescan pattern every sibling proof type already uses) was attempted together with the M-7 nonce-aware timeout fix and an M-14 dead-code removal. That combined change broke three existing tests and was reverted rather than debugged under time pressure. Only a narrower, independently-safe subset landed: a surgical patch for M-3 (non-lossy cursor rewind) plus unrelated memoization/metrics/test fixes. **M-16's own P1 rating is only partially addressed** - the persistent-cursor design itself, and the M-7/M-14 symptoms it also breeds, remain unremoved. 1. **M-16 (P1)** `pkg/maintainer/spv/reservation_proof_loop.go:227-246` - `reservationProofScanState`'s persistent cursor is the structural root cause of M-3 (fixed surgically) and M-7 (below). Removing it in favor of the stateless bounded-rescan pattern is what broke 3 tests on first attempt and remains unimplemented. 2. **M-7 (P2)** `reservation_action_timeout_watch.go:260-281` - `CheckReservationActionTimeouts` deletes `pendingActions` entries on 3 of 4 non-notifying outcomes without asserting the tracked `requestNonce` against the freshly-derived one; same root cause as M-3. 3. **P2** `reservation_action_timeout_watch.go:370` + `reservation_wiring.go:38-49` - the timeout watcher's `WalletMembersResolver` only resolves wallets the local operator co-signs; an offline/disabled/colluding wallet's own operators get zero independent timeout coverage. 4. **P2** dead-code cluster in `reservation_proof_loop.go` / `reservation_proof_loop_test.go` - `findReservationAcceptanceTransaction`, `findReservationReanchorTransaction`, and their wrapper helpers have zero production callers; 14 tests exercise the unused wrapper instead of the `isMatching*` predicates actually called in production. 5. **P2** `reservation_proof_loop.go:612,~817` - two tautological guards are algebraically always-false, masking that the real enforced constraint is only `0 < fee <= TxMaxFee`. 6. **P2** `reservation_wiring.go:237-320` `startStaleDepositPoll` - the entire loop body runs untested inside a goroutine; existing tests assert only that the goroutine starts. 7. **P3** `reservation_action_timeout_watch.go:18-20` - unused "backward-compatibility alias" constant, zero references. 8. **P3** `reservation_proof_loop.go:644` - duplicated, truncated comment fragment left by a merge. ## Known conflicts with other open PRs in this stack - read before merging This branched from `reservations-epic` at `bb3dcb398`. Three other efforts are in flight against overlapping code and were **not** reconciled here, since they belong to PRs this one doesn't own: ### 1. `pkg/tbtc/coordination.go` vs #4278 (hard conflict, not cosmetic) #4278 ("remove frequency gate on reservation checklist actions") drops `&& windowIndex%frequencyWindows == 0` from the reservation-actions checklist gate (custody-critical, should run every window like `ActionRedemption`) but its diff still references the old single `ReservationsActivationBlock` constant. This PR's `602d0ef11` independently rewrote that same `if` into `reservationsActivationBlock(ce.ethereumNetwork)`, a per-network table lookup (`ethereum.Mainnet: 26500000`, everything else defaults to 0). **A conflict resolution that naively favors this PR's side of that hunk silently reinstates the frequency gate #4278 deliberately removed.** Combined resolution (verified against both intents): ```go // Reservation actions (acceptance, re-anchor) are custody-critical like // Redemption and are checked on every coordination window once the // activation block is reached, not frequency-gated like the // throughput-driven DepositSweep/MovedFundsSweep/MovingFunds actions // above: a delayed reservation acceptance or re-anchor risks the // on-chain ReservationActionTimeout backstop firing before the wallet // subsystem gets a chance to act. The activation block is a per-network // table (reservationsActivationBlock), not a single global constant, but // it is still config-independent and globally observable from chain // height alone -- which is what keeps leader and follower checklists in // agreement without relying on local config. if coordinationBlock >= reservationsActivationBlock(ce.ethereumNetwork) { actions = append(actions, ActionReservationAnchor) actions = append(actions, ActionReservationReanchor) } ``` ### 2. `pkg/tbtc/marshaling.go` vs #4278 (duplicate, this PR's version wins) #4278 independently adds the same 4 missing `Marshal`/`Unmarshal` doc comments this PR's `7cbb8cc2f` adds, but comment-only and with a capitalization bug (lowercases the exported type name, e.g. `"...converts the reservationAnchorProposal..."`). This PR's version is a superset: correctly capitalized comments plus the actual nil-guard/zero-hash-rejection logic #4278 doesn't have. On merge, take this PR's 4 lines, drop #4278's. ### 3. `pkg/tbtcpg/reservation_acceptance_test.go` vs #4280 (whole-file conflict + one real design decision) #4280 ("M2 test-coverage backfill") independently rewrote large parts of the same shared test harness this PR's `726f05ed7` touched - the same `reservationAcceptanceLocalChain` type, constructor, and ~14 shared methods, plus `scenarioReservationAcceptanceChain`/`registerReservedDeposits`/ `expectedAnchorsEqual`. This is a heavy line-level conflict across the whole file, not just redundant test names. Specifics: - `TestReservationAcceptanceTask_AmountCapBoundaries` (this PR, cap boundaries only) is a strict subset of #4280's `TestReservationAcceptanceTask_BoundaryChecks` (adds `MaxReservationsPerWallet`, net-of-fee `ReservationMinAmount`, `ActiveReservationsCount`). Left in place rather than deleted preemptively - #4280 is still open and two-deep-stacked (on #4278, also open) and could stall or be reworked; delete this PR's version only in the merge that actually lands #4280. - This PR's `TestReservationAcceptanceTask_VaultNotConfigured_ZeroAddress` (finding: dead vault-not-configured guard) has no equivalent on #4280's side - a "just take #4280's file" resolution silently drops it. - **Real design decision, not just a merge conflict:** #4280's `TestReservationAcceptanceTask_Stateless_PastEventsError` exercises `PastReservationAcceptanceRequestedEvents` returning an error and asserts fail-closed skip-on-error. This PR's `hasPendingAction` (from `726f05ed7`) no longer calls `PastReservationAcceptanceRequestedEvents` at all - it uses a different, generation-scoped pending-action check instead. Ported onto this PR's code as-is, that test would either pass vacuously or fail for an unrelated reason. **This PR intentionally left `PastReservationAcceptanceRequestedEvents` on the `tbtcpg.Chain` interface (`chain.go:263`) and the test double's `acceptanceEvents`/`acceptanceEventsErr` fields in place, undeleted, even though they now have zero production callers** - removing them here would have foreclosed reconciling #4280's test against whichever pending-action mechanism is ultimately kept. Whoever merges this PR and #4280 needs to pick one mechanism and either delete the losing side's interface method/test or keep both if there's a reason for two independent checks. ## Testing - `go build ./...`, `go vet ./...`: clean. - `go test ./...`: full repo suite, 0 failures (verified at closeout after every commit landed).
piotr-roslaniec
marked this pull request as ready for review
September 3, 2026 15:42
…vation-test-coverage-backfill # Conflicts: # pkg/tbtcpg/reservation_acceptance.go # pkg/tbtcpg/reservation_acceptance_test.go
The 'records acceptance request, skipping duplicate on subsequent run' test relied on hasPendingAction's fail-closed default on a missing GetReservationAction record, not on the intended pending-state detection path - its comment still described the removed PastReservationAcceptanceRequestedEvents mechanism. Explicitly set the action record to Pending after the first run so the second run's dedup assertion genuinely exercises hasPendingAction's happy path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
implementation-plan.mdMilestone 2's test-coverage backfill:7 of the 8 listed items (item 8's scope narrowed - see below). One
earlier-planned item, a golden-value dedup test for
AssembleReservationAnchorTransaction, was obsoleted whenproposeReservationAcceptancewas switched to call the exportedtbtc.AssembleReservationAnchorTransactiondirectly, removing the second,unexported copy the dedup test would have compared against; it was not
silently dropped. Merged up to date with
m1/reservation-multisigner-integration-test(#4279), tip
9e42103e8.The 8th item (
ValidateReservationAnchorProposal/ValidateReservationReanchorProposaltests) is explicitly deferred - itneeds
go-ethereumsimulated-backend test infrastructure that doesn'texist anywhere in
pkg/chain/ethereumtoday, well beyond the plan's0.5-day estimate. See
docs/spec/reservations/m1-keep-core-readiness/01-gap-analysis.md'snew Minor row for the full finding.
Change
pkg/tbtc/reservation_test.goTestAssembleReservationAnchorTransaction: happy-path output shape(1-in-1-out, deposit value minus fee, P2WPKH to the target wallet).
TestAssembleReservationReanchorTransaction: same shape assertion forthe re-anchor sibling.
pkg/chain/ethereum/tbtc_test.goTestConvertReservationParametersFromAbiType: full 10-tuple fieldmapping, every field a distinct non-zero value so a swapped or dropped
field can't hide behind a shared zero default.
TestConvertReservationFromAbiType_DropsCumulativeReanchorFee: pins theintentional
CumulativeReanchorFeeomission and verifies every otherfield maps correctly around it.
pkg/tbtcpg/reservation_acceptance_test.goTestReservationAcceptanceTask_BoundaryChecks: table coveringat-limit/one-over-limit boundary crossings for
MaxReservationsPerWallet,ReservationMinAmount,ReservationMaxTotalAmount,ReservationMaxSingleAmount,MaxReservationsAmountPerWallet, andActiveReservationsCount, plus the net-of-fee minimum check inproposeReservationAcceptance-TestReservationAcceptanceTask_BoundedLookbackonly ever used thesefields as fixture data, never at the actual boundary.
TestReservationAcceptanceTask_ReservationParametersFetchedLive: runsthe same task twice against the same deposit, mutating
ReservationMinAmountbetween calls - verifies a governance-drivenparameter change takes effect on the very next
Run()call, with noleftover value from a prior run observable in the eligibility decision.
TestReservationAcceptanceTask_AnchorTransactionAssembly: end-to-endwiring test - runs the task to get a
ReservationAnchorProposal, thenre-assembles and signs the anchor transaction via the exported
tbtc.AssembleReservationAnchorTransaction, and asserts the resultingsigned transaction is a valid 1-input-1-output transaction paying the
correct wallet P2WPKH output script with value equal to deposit amount
minus the anchor fee.
Testing
go test ./pkg/tbtc/... ./pkg/tbtcpg/... ./pkg/chain/ethereum/...:280/280 pass.
go build ./...&&go test ./...: full repo, 49 packages, zeroFAIL.gofmt -l/go vet: clean on all changed/new files.Not in this PR
ValidateReservationAnchorProposal/ValidateReservationReanchorProposaltests - deferred, documented in the gap-analysis doc.